/** @file    SocketTcpLS.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SocketTcpLS.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the wosh::network::libsockets::SocketTcpLS class.
 * The header for this class can be found in SocketTcpLS.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/network/libsockets/SocketTcpLS.h>
// #include <framework/network/libsockets/ServerTcp.h>

 #include <core/ObjectAllocator.h>
 #include <framework/network/NetworkUtilities.h>
 #include <core/Utilities.h>

 #include <Sockets/TcpSocket.h>
 #include <Sockets/Utility.h>
 #include <Sockets/SocketHandler.h>


class SocketTcpWrapper : public Sockets::TcpSocket {
	public:
		SocketTcpWrapper( Sockets::SocketHandler& m ) : Sockets::TcpSocket(m) {
			this->parent = NULL;
		 }
		virtual ~SocketTcpWrapper() { }

	public:

		void OnConnect() {

		 }

		void OnRawData( const char *buf, size_t len ) {

		 }
/*
		void OnRawData(const char *p,size_t l, struct sockaddr *sa_from, socklen_t sa_len ) {
			if ( this->parent != NULL )
				this->parent->OnRawData(p,l,sa_from,sa_len);
		 }
*/

	public:
		wosh::network::libsockets::SocketTcpLS* parent;

};

using namespace std;
namespace wosh {
 namespace network {
  namespace libsockets {

  WOSH_REGISTER(wosh::network::libsockets::SocketTcpLS, "wosh::network::SocketTcp", 1.0, _static_SocketTcpLS )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SocketTcpLS::SocketTcpLS() : wosh::ThreadImpl(NULL, "SocketTcpLS.Thread"), wosh::network::SocketTcp()
 {
	this->sHandler = new Sockets::SocketHandler();
	this->socketWapper = new SocketTcpWrapper(*this->sHandler);
	this->socketWapper->parent = this;
 }

SocketTcpLS::~SocketTcpLS()
 {
	if ( this->isConnected() )
		disconnect();

	delete this->socketWapper; this->socketWapper = NULL;

	delete this->sHandler; this->sHandler = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SocketTcpLS::connect( unsigned long timeout )
 {
	if ( isThreadRunning() ) {
#ifdef _SocketTcp_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":connect() Thread is running!" );
#endif
		return WRET_ERR_ILLEGAL_USE;
	 }
	if ( isConnected() ) {
#ifdef _SocketTcp_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":connect() Already connected!" );
#endif
		return WRET_ERR_ILLEGAL_USE;
	 }
#ifdef _SocketTcp_DEBUG_ENABLED
	Log(LOG_INFO, ":connect() to %s", this->address.c_str() );
#endif
	WRESULT ret = startThread(timeout);
#ifdef _SocketTcp_DEBUG_ENABLED
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":connect(%s,%u) Listening FAILED#%d [TimedOut]!", this->address.c_str(), timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":connect(%s) Asynch Listening..", this->address.c_str() );
		else
			Log(LOG_INFO, ":connect(%s) Listening succedded.", this->address.c_str() );
	 }
#endif
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SocketTcpLS::disconnect( unsigned long timeout )
 {
	if ( !isThreadRunning() ) {
		Log(LOG_CRITICAL, ":disconnect() Thread is NOT running!" );
		return WRET_ERR_ILLEGAL_USE;
	 }
	Log(LOG_VERBOSE, ":disconnect(%u) ", timeout );
	WRESULT ret = ThreadImpl::quitThread(timeout);
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":disconnect(%u) FAILED#%d [TimedOut]!", timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":disconnect() Asynch Disconnecting.." );
		else
			Log(LOG_INFO, ":disconnect() Disconnecting succedded." );
	 }
	return ret;
 }

WRESULT SocketTcpLS::setSocket( int socket_descriptor )
 {

	return WRET_OK;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// BINDING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN

bool SocketTcpLS::initializingThread()
 {
//	this->sHandler->Add(this->socketWapper);
//	this->sHandler->Select(1,0);

	port_t p = (unsigned int)this->port;
#ifdef _SocketTcp_DEBUG_ENABLED
	Log(LOG_VERBOSE, ":runThread() Connecting Socket to '%s' at port %d..", this->address.c_str(), this->port );
#endif

#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
	//this->socketWapper->SetIpv6();
#endif // IPPROTO_IPV6
#endif // ENABLE_IPV6

	bool binded = this->socketWapper->Open(this->address.c_str(),p);
	if ( !binded ) {
#ifdef _SocketTcp_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":runThread() FAILED : Connecting Socket to '%s' at port %d.. []", this->address.c_str(), p );
#endif
		return false;
	 }
	this->sHandler->Add(this->socketWapper);
	this->sHandler->Select(1,0);
 	return true;
 }

void SocketTcpLS::runThread()
 {
	while( this->running && this->sHandler->GetCount() ) {
		this->sHandler->Select(1,0);
	 }
 }

void SocketTcpLS::exitingThread()
 { 
#ifdef _SocketTcp_DEBUG_ENABLED
	Log(LOG_VERBOSE, ":exitingThread() Closing Socket.." );
#endif
	int closed = this->socketWapper->Close();
#ifdef _SocketTcp_DEBUG_ENABLED
	if ( closed ) {
		Log(LOG_CRITICAL, ":exitingThread() FAILED#%d Closing Socket..", closed );
	 }
#endif
 }

////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////// INCOMING DATAGRAMS

int64 SocketTcpLS::read( char* output, int64 data_len )
 {
	return (int64)this->socketWapper->ReadInput( output, (size_t)data_len );
 }

////////////////////////////////////////////////////////////////////////////////////////// INCOMING DATAGRAMS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int SocketTcpLS::sendDatagram( const char* data, int size ) {
	if ( size <= 0 ) size = strlen(data);
	this->socketWapper->SendBuf(data, size);
	return size;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string SocketTcpLS::getPeerAddressPort() const {
	return Utilities::format("%s:%p", this->socketWapper->GetRemoteAddress().c_str(), (unsigned int)this->socketWapper->GetRemotePort());
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool SocketTcpLS::isConnected() const {
	return ( isThreadRunning() && this->socketWapper->IsConnected() );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

  }; // namespace libsockets
 }; // namespace network
}; // namespace wosh
